package Sonnet;

import java.awt.Dimension;

import javafx.application.Application;

import javafx.application.Platform;
import javafx.scene.*;
import javafx.scene.Scene;
import javafx.stage.Stage;

import javafx.fxml.FXMLLoader;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.scene.SnapshotParameters;

import java.awt.*;
import java.awt.image.*;
import java.awt.event.ActionEvent;
import java.awt.Dimension;
import java.lang.Object;
import java.io.InputStream;
import java.io.File;
import javax.imageio.ImageIO;//Keep For .png Image Creation
import java.io.IOException;
import java.nio.ByteBuffer;

import javafx.scene.paint.Color;
import javafx.geometry.Rectangle2D;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.image.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.image.WritableImage.*;
import javafx.scene.input.KeyEvent;
//import javafx.scene.layout.VBox;
import javafx.scene.text.*;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.scene.layout.*;
//import javafx.scene.paint.Color;
//import javafx.scene.shape.*;

//import java.util.Timer;
//import java.util.TimerTask;
import javax.swing.JFileChooser;
//import javafx.stage.DirectoryChooser;
//import javafx.stage.FileChooser;
import javafx.embed.swing.SwingFXUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

import org.apache.commons.io.FileUtils;

import org.openqa.selenium.OutputType;

import org.openqa.selenium.TakesScreenshot;


import org.jcodec.api.awt.SequenceEncoder;//Keep For .mp4 Video Creation

public class Sonnet extends Application
{

	@Override
	public void start(Stage stage) throws IOException
	{
		Thread thread = new Thread(() -> {
			try { Thread.sleep(400); }
			catch (InterruptedException exc) { throw new Error("Unexpected interruption", exc); }
			// Update text on FX Application Thread:
			Platform.runLater(new Runnable() {
				@Override
				public void run()
				{
					try { RainbowSystem(stage); }
					catch(IOException ie) { ie.printStackTrace(); }
				}
			});
		});
		thread.setDaemon(true);
		thread.start();
	}

	static void RainbowSystem(Stage primaryStage) throws IOException
	{
		primaryStage.setTitle("Sonnet");
		primaryStage.setWidth(1920);
		primaryStage.setHeight(1080);

		// This is how you know how much screen you can write to.
		Screen screen = Screen.getPrimary();
		Rectangle2D bounds = screen.getVisualBounds();
		System.out.println(bounds);

		primaryStage.setFullScreen(true);

		double fRateDivisor = 124;//2;//22<- 444 HERE HERE HERE DETERMINES FRAME RATE AND RAINBOW MOVEMENT RATE For Rainbow Lights

		FXMLLoader loader = new FXMLLoader(Sonnet.class.getResource("main.fxml"));


		StackPane stackPane[] = new StackPane[(int)(fRateDivisor + 1)];
		stackPane[0] = new StackPane();
		stackPane[0].setPrefSize(1920, 1080);//stackPane.setPrefSize(1920, 1080);//stackPane.setPrefSize(960, 540);

		Dimension dimension = new Dimension( (int)bounds.getWidth(), (int)bounds.getHeight() );
		dimension.width = (int)bounds.getWidth();
		dimension.height = (int)bounds.getHeight() + 40;
		Dimension Dim = dimension;

		//Image image = new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Pixies%20Text.png");

		WebView browser = new WebView();
		browser.setPrefSize( bounds.getWidth(), bounds.getHeight() );
		WebEngine webEngine = browser.getEngine();
		SnapshotParameters sP = new SnapshotParameters();
		sP.setViewport(bounds);
		webEngine.load("http://www.pixies.zone/PixiesTest.html");

		WritableImage origionalTextPicture = new WritableImage( dimension.width, dimension.height );
		//Image origionalBrowserImage = new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Pixies%20Text.png");//browser.snapshot(sP, origionalTextPicture);//new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Pixies%20Text.png");//
		//Image origionalBrowserImage = new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Test.png");//browser.snapshot(sP, origionalTextPicture);//new Image("http://www.nanofirm.org/scimagorder.com/Pictures/Pixies%20Text.png");//
		//Image origionalBrowserImage = browser.snapshot(sP, origionalTextPicture);
		Image origionalBrowserImage = new Image("http://www.nanofirm.org/scimagorder.com/Pictures/FatLevels.png");
		PixelReader origionalpr = origionalBrowserImage.getPixelReader();
		PixelWriter origionalpw = origionalTextPicture.getPixelWriter();
		ImageView origionalImageView = new ImageView();
		origionalImageView.setImage(origionalBrowserImage);

		JFileChooser jFileChooser = new JFileChooser();
		String path = "C:\\TestVideo.mp4", text = "Sky.Netarianism";//"C:\\TestImage.png"//"C:\\TestVideo.mp4"//"C:\\TEMP_Sonnet.mp4";
		File selectedFile = new File (path);

		jFileChooser.setSelectedFile(selectedFile);
		int returnValue = jFileChooser.showSaveDialog(null);
		if (returnValue == JFileChooser.APPROVE_OPTION)
		{
			selectedFile = jFileChooser.getSelectedFile();
			System.out.println(selectedFile.getName());
			System.out.println(selectedFile.getPath());
		}

		SequenceEncoder enc = new SequenceEncoder(selectedFile);

		Scene scene[] = new Scene[(int)(fRateDivisor + 1)];
		scene[0] = new Scene(stackPane[0]);

		//scene[0].setRoot(browser);

		stackPane[0].getChildren().add(origionalImageView);
		//stackPane[0].getChildren().add(browser);
		primaryStage.setScene(scene[0]);

		primaryStage.setFullScreen(true);
		primaryStage.show();

		scene[0].snapshot(origionalTextPicture);

		/*File tempFile;
		//WebDriver driver=new FirefoxDriver();

		// Maximize the window
		driver.manage().window().maximize();

		// Pass the url
		driver.get("http://www.pixies.zone/PixiesTest.html");

		// Take screenshot and store as a file format
		File src= ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
		try {// now copy the  screenshot to desired location using copyFile //method
			FileUtils.copyFile(src, new File("C:/TempImage.png"));
		}

		catch (IOException e)
		{
			System.out.println(e.getMessage());

		}// */


		WritableImage writableImage = origionalTextPicture;
		PixelReader pr = writableImage.getPixelReader();
		PixelWriter pw = writableImage.getPixelWriter();
		ImageView imageView = new ImageView();
		imageView.setImage(writableImage);

		ColorSlider black = new ColorSlider( new double[]{0,0,0} );

		ColorSlider color = black;

		ColorSlider white = new ColorSlider( new double[]{255,255,255} );
		ColorSlider skyblue = new ColorSlider( new double[]{134,206,249} );
		ColorSlider pink = new ColorSlider( new double[]{255,64,143} );
		ColorSlider blue = new ColorSlider( new double[]{0,0,192} );
		ColorSlider red = new ColorSlider( new double[]{193,0,0} );
		ColorSlider purple = new ColorSlider( new double[]{122,0,178} );
		ColorSlider orange = new ColorSlider( new double[]{255,134,0} );
		ColorSlider green = new ColorSlider( new double[]{0,133,50} );
		ColorSlider yellow = new ColorSlider( new double[]{246,252,15} );
		ColorSlider teal = new ColorSlider( new double[]{3,224,149} );
		ColorSlider bronze = new ColorSlider( new double[]{152,123,46} );
		ColorSlider silver = new ColorSlider( new double[]{193,192,192} );
		ColorSlider gold = new ColorSlider( new double[]{205,191,44} );
		ColorSlider lime = new ColorSlider( new double[]{126,193,33} );
		ColorSlider brown = new ColorSlider( new double[]{131,103,71} );

		int pixiePink = (int)(255 * 16777216) + (int)(255 * 65536) + (int)(0 * 256) + 255;

		int numberOfColorsInTheRainbow = 7;//asdf Always Worry About This!

		Dimension HalfedDim = new Dimension();
		HalfedDim.width = Math.round( ( Dim.width + 1 ) / 2 - 1 );
		HalfedDim.height = Math.round( ( Dim.height + 1 ) / 2 - 1 );
		int x = 0, y = 0, xMinusxSlider = 0, yMinusxSlider = 0, cxMx = 0, ySlider = 0, yMinusySlider = 0, cyMy = 0,  b = 0, xSliderFactor = 1, xSliderBackAndFourth = 1, fixedOfabcd = 4, numOfabcd = 4, colorIntervalDivisor = 1, aCounter = 0, bCounter = 0, cCounter = 0;//cxMx Corrected xMinusSlider for determining color
		double xSlider = 0, a = 0, colorInterval = Dim.width / ( numberOfColorsInTheRainbow - 1 ), numberOfClockHands = 16, abcd = 0, abc = 0, rCheck = 0, aAngle = 0, bAngle = 0, newx = 0, newy = 0, abc2 = 0, aIncrementAngle = 0, bIncrementAngle = 0, aTan = 0;
		boolean TrispectralRainbow = false;
		if (TrispectralRainbow == true)
			colorInterval = colorInterval / 3;//For Trispectral Rainbow.
		double colorDisplacement = 0;//For Rainbow Disk And 0 Rainbow Clock
		double radialDistance = Math.pow(Math.pow(HalfedDim.width + 1, 2) + Math.pow(HalfedDim.height + 1, 2), 0.5);
		double angle1 = 0, angle2 = 0, angle3 = 0, angle4 = 0, aAngleCheck = 0;
		double radius = 0;
		double fullAngle = Math.PI * radialDistance;
		double interval = fullAngle / 2 / fRateDivisor;//Last Division Determines The Frame Rate
		colorInterval = fullAngle / ( numberOfColorsInTheRainbow - 1 );//For Rainbow Clock And Spiral
		///double interval = radialDistance * 2 * Math.PI / 1444;//1444 Instead Of 14 Gets The Frame Rate To Have Contiguous Motion For Rainbow Clock
		//double fullAngle = 2 * Math.PI * radialDistance;//For Rainbow Clock
		//colorInterval = fullAngle / ( numberOfColorsInTheRainbow - 1 ) / numberOfClockHands;//For Rainbow Clock
		//for (xSlider = (int)Math.round(colorDisplacement) + radialDistance * 2 * Math.PI / numberOfClockHands / 2; xSlider < 1.5 * ( radialDistance * 2 * Math.PI / numberOfClockHands ); xSlider++)// < For Rainbow Clock, xSlider = (int)Math.round(colorDisplacement); For Rings -144 Use cy And < HalfedDim.height Instead of Dim.height For Circular Rainbow Use Dim.width < For Rainbow Slider And Fractal //Start At -144 For Videos And Pictures
		while ( xSlider < fullAngle )
		{//0.175979734764 * 
			//Dim = f.getSize();//Dim = f.getContentPane().getSize();
			HalfedDim.width = Math.round( ( Dim.width + 1 ) / 2 - 1 );
			HalfedDim.height = Math.round( ( Dim.height + 1 ) / 2 - 1 );
			radialDistance = Math.pow(Math.pow(HalfedDim.width + 1, 2) + Math.pow(HalfedDim.height + 1, 2), 0.5);
			rCheck = HalfedDim.height / fixedOfabcd;
			numOfabcd = (int)Math.ceil(radialDistance / HalfedDim.height);
			//double[] angle = new double[numOfabcd], abcdefg = new double[numOfabcd];
			double angle = 0;
			colorDisplacement = 0;//For Rainbow Disk
			for (x = 0; x < Dim.width; x++)//Comment Out Here For Circular Rainbow
			{//Comment Out Here For Circular Rainbow
				for (y = 0; y < Dim.height; y++)
				{	
					angle = ( Math.atan2( (x - HalfedDim.width + 1) , (y - HalfedDim.height + 1 ) ) + Math.PI ) * radialDistance + xSlider;//For Rainbow Strobe Effect Use: angle = Math.atan2( (x - HalfedDim.width + 1) , (y - Dim.height * 4 / 3 + 1 ) );// For Rising Sun + 1
					if (angle < 0)
						angle = angle + fullAngle;
					else
						if (angle > fullAngle)
							angle = angle - fullAngle;
					radius = Math.pow( Math.pow( (x - HalfedDim.width + 1) , 2) + Math.pow( (y - HalfedDim.height + 1) , 2) , 0.5);
					aAngle = fullAngle;
					colorIntervalDivisor = 1;
					while ( radius > rCheck )
					{
						aAngle = aAngle / 4;
						colorIntervalDivisor++;
						while (angle > aAngle)
						{
							angle = angle - aAngle;
							radius = radius - rCheck;
						}
						//radius = radius - rCheck;
					}
					abcd = angle;
					while ( abcd < colorDisplacement)//For Rainbow Ring
						abcd = abcd + aAngle - colorDisplacement;//BAD COMMENT: For Rainbow Ring Take Out - 38
					while ( abcd > aAngle )//radius > fullAngle For Non Infinite Number Theorem
						abcd = abcd - aAngle - colorDisplacement; //radius = radius - fullAngle; For Non Infinite Number Theorem //For Rainbow Clock Comment Out To Here. Here -- */
					if ( abcd >= colorDisplacement && abcd < (numberOfColorsInTheRainbow - 1) * colorInterval  + colorDisplacement)//For Purple Outside Ring
					{
						if ( origionalpr.getArgb(x, y) == pixiePink )
							if ( abcd < colorInterval  + colorDisplacement)//For Normal Rainbow Use This Instead Of The Others
								color.ColorSliderFunction(colorInterval, abcd - colorDisplacement, purple, blue);
							else if ( abcd < ( 2 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, abcd - colorInterval - colorDisplacement, blue, teal);
							else if ( abcd < ( 3 * colorInterval  + colorDisplacement) )		
								color.ColorSliderFunction(colorInterval, abcd - 2 * colorInterval - colorDisplacement, teal, pink);
							else if ( abcd < ( 4 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, abcd - 3 * colorInterval - colorDisplacement, pink, yellow);
							else if ( abcd < ( 5 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, abcd - 4 * colorInterval - colorDisplacement, yellow, orange);
							else
								color.ColorSliderFunction(colorInterval, abcd - 5 * colorInterval - colorDisplacement, orange, red);
						else//*/
							if ( abcd < colorInterval  + colorDisplacement)//For Normal Rainbow Use This Instead Of The Others
								color.ColorSliderFunction(colorInterval, abcd - colorDisplacement, red, orange);
							else if ( abcd < ( 2 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, abcd - colorInterval - colorDisplacement, orange, yellow);
							else if ( abcd < ( 3 * colorInterval  + colorDisplacement) )		
								color.ColorSliderFunction(colorInterval, abcd - 2 * colorInterval - colorDisplacement, yellow, green);
							else if ( abcd < ( 4 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, abcd - 3 * colorInterval - colorDisplacement, green, teal);
							else if ( abcd < ( 5 * colorInterval  + colorDisplacement) )
								color.ColorSliderFunction(colorInterval, abcd - 4 * colorInterval - colorDisplacement, teal, blue);
							else
								color.ColorSliderFunction(colorInterval, abcd - 5 * colorInterval - colorDisplacement, blue, purple);//*
					}
					//Use To //*/ For Rainbow Clock
					/*double angle = ( Math.atan2( (x - HalfedDim.width + 1) , (y - HalfedDim.height + 1 ) ) + Math.PI ) * radialDistance + xSlider;
					if (angle < 0)
						angle = angle + fullAngle / numberOfClockHands;
					else
						while (angle > fullAngle / numberOfClockHands)
							angle = angle - fullAngle / numberOfClockHands;
					//angle = 0;//Testing Purposes Only.
					if ( angle < colorInterval)//For Normal Rainbow Use This Instead Of The Others
						color.ColorSliderFunction(colorInterval, angle, red, orange);
					else if ( angle < 2 * colorInterval )
						color.ColorSliderFunction(colorInterval, angle - colorInterval, orange, yellow);
					else if ( angle < 3 * colorInterval )		
						color.ColorSliderFunction(colorInterval, angle - 2 * colorInterval, yellow, green);
					else if ( angle < 4 * colorInterval )
						color.ColorSliderFunction(colorInterval, angle - 3 * colorInterval, green, teal);
					else if ( angle <  5 * colorInterval )
						color.ColorSliderFunction(colorInterval, angle - 4 * colorInterval, teal, blue);
					else
						color.ColorSliderFunction(colorInterval, angle - 5 * colorInterval, blue, purple);//*/

					pw.setArgb( x, y, (int)(255 * 16777216 + (int)color.RGB[0] * 65536 + (int)color.RGB[1] * 256 + (int)color.RGB[2]));//setArgb
				}
			}
			imageView.setImage(writableImage);

			stackPane[(int)(fRateDivisor * xSlider/fullAngle) + 1] = new StackPane();
			stackPane[(int)(fRateDivisor * xSlider/fullAngle) + 1].getChildren().add(imageView);
			//stackPane[(int)(fRateDivisor * xSlider/fullAngle) + 1].getChildren().add(browser);
			scene[(int)(fRateDivisor * xSlider/fullAngle) + 1] = new Scene(stackPane[(int)(fRateDivisor * xSlider/fullAngle) + 1]);
			primaryStage.setScene(scene[(int)(fRateDivisor * xSlider/fullAngle) + 1]);

			System.out.println(Dim);

			primaryStage.setFullScreen(true);

			primaryStage.show();

			if (xSlider == (int)Math.round(colorDisplacement) + radialDistance * 2 * Math.PI / numberOfClockHands / 2)
			{
				File outputFile = new File("C:/TestImage.png");
				BufferedImage bImage = SwingFXUtils.fromFXImage(scene[(int)(fRateDivisor * xSlider/fullAngle) + 1].snapshot(null), null);
				try {
					ImageIO.write(bImage, "png", outputFile);
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
			enc.encodeImage( SwingFXUtils.fromFXImage(scene[(int)(fRateDivisor * xSlider/fullAngle) + 1].snapshot(null), null ) );
			//enc.encodeImage( SwingFXUtils.fromFXImage( imageView.getImage(), null ) );


			xSlider = xSlider + interval;
			System.out.println(Dim);
			//}
			//Thread.sleep(2000);}
			//catch (Exception ex) {ex.printStackTrace();}

		}
		enc.finish();		
		/*scene.setOnKeyReleased(new EventHandler<KeyEvent>()
		{
			@Override
			public void handle(KeyEvent event)
			{
				if ( event.isAltDown() == true)
				{
					switch (event.getCode())
					{
						case ENTER: primaryStage.setFullScreen(!primaryStage.isFullScreen()); break;
					}
				}
			}
		});//*/
	}

	public static void main(String[] args) throws IOException
	{
		launch(args);
	}
}